﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Jayden.Dll.Web.HtmlControls;
using Jayden.Dll.Core.Persistence.Models;
using Jayden.Dll.Web.HtmlControls.Generics;
using Jayden.Dll.Core.Extensions;
using Jayden.Dll.Web.Enums;
using System.Reflection;

namespace Jayden.Dll.Web.Controls
{
	public class EntityForm : Form
	{

		protected Entity m_Entity;

		public EntityForm() { }
		public EntityForm(Entity entity) { m_Entity = entity; }

		public bool BuildControl(HttpHandlerContext context)
		{
			string entity = GetServerAttribute("entity", null);
			if (entity != null)
				m_Entity = context.UnitOfWork.Model.GetEntity(entity);
			if (m_Entity != null)
			{
				EntityFormModeType mode = EntityFormModeType.Create;

				List<Property> properties = new List<Property>(m_Entity.Properties);
				FilterProperties(mode, properties);
				SortProperties(mode, properties);

				Fieldset fieldset = AddControl<Fieldset>();
				fieldset.AddLegend(m_Entity.Name);
				fieldset.Classes.Add(mode.ToString().ToLowerFirst());

				foreach (Property property in properties)
				{
					Div div = fieldset.AddControl<Div>();
					Label label = div.AddControl<Label>();
					label.InnerText = GetPropertyLabel(mode, property);
					HtmlElement control = BuildPropertyElement(mode, property, null);
					div.AddControl(control);
				}
				return true;
			}

			return false;
		}

		public override void ProcessHtmlRequest(HttpHandlerContext context)
		{
			BuildControl(context);
			base.ProcessHtmlRequest(context);
		}

		protected virtual void FilterProperties(EntityFormModeType mode, List<Property> properties)
		{
			string filter = GetServerAttribute("filter", null);
			if (!string.IsNullOrWhiteSpace(filter))
			{
				HashSet<string> ids = new HashSet<string>(filter.Split(','));
				for(int c=0;c<properties.Count;c++)
					if (ids.Contains(properties[c].Name))
					{
						properties.RemoveAt(c);
						c--;
					}
			}
		}
		protected virtual void SortProperties(EntityFormModeType mode, List<Property> properties) { }
		protected virtual string GetPropertyLabel(EntityFormModeType mode, Property property) { return property.Name + ":"; }
		protected virtual string GetPropertyName(EntityFormModeType mode, Property property) { return property.Name.CamelCase().ToLowerFirst(); }
		protected virtual string GetStringValue(EntityFormModeType mode, Property property, object obj) { return (property.GetValue(obj) ?? "").ToString(); }

		protected virtual HtmlElement BuildPropertyElement(EntityFormModeType mode, Property property, object obj)
		{
			string propertyName = GetPropertyName(mode,property);
			string stringValue = string.Empty;
			if(obj != null)
				stringValue = GetStringValue(mode, property, obj);
			if (property.PropertyInfo.PropertyType.IsEnum)
			{
				// create a select for enums
				Select select = new Select();
				MemberInfo[] values = property.PropertyInfo.PropertyType.GetMembers(BindingFlags.Public | BindingFlags.Static);
				foreach (MemberInfo info in values)
					select.AddOption(info.Name, info.Name);
				return select;
			}
			else
			{
				// an input for the rest
				HtmlInputType type = HtmlInputType.Text;
				// checkbox for boolean
				if (property.PropertyInfo.PropertyType == typeof(bool))
					type = HtmlInputType.Checkbox;
				else
				{
					// numeric from type
					TypeCode typeCode = Type.GetTypeCode(property.PropertyInfo.PropertyType);
					if (typeCode == TypeCode.Byte || typeCode == TypeCode.Decimal || typeCode == TypeCode.Double || typeCode == TypeCode.Int16 ||
						typeCode == TypeCode.Int32 || typeCode == TypeCode.Int64 || typeCode == TypeCode.SByte || typeCode == TypeCode.Single ||
						typeCode == TypeCode.UInt16 || typeCode == TypeCode.UInt32 || typeCode == TypeCode.UInt64)
						type = HtmlInputType.Number;
				}
				Input input = new Input(type, propertyName, stringValue);
				// readonly
				if (mode == EntityFormModeType.Read)
					input.ReadOnly = true;
				return input;
			}
		}
	}
}
